// // Copyright 2014 The Flutter Authors. All rights reserved.
// // Use of this source code is governed by a BSD-style license that can be
// // found in the LICENSE file.

// // @dart = 2.14

// import 'package:flutter/animation.dart';
// import 'package:flutter/foundation.dart';
// import 'package:flutter/widgets.dart';

// /// Signature for the builder callback used by [AnimatedList].
// typedef AnimatedListItemBuilder = Widget Function(
//     BuildContext context, int index, Animation<double> animation);

// /// Signature for the builder callback used by [AnimatedListState.removeItem].
// typedef AnimatedListRemovedItemBuilder = Widget Function(
//     BuildContext context, Animation<double> animation);

// // The default insert/remove animation duration.
// const Duration _kDuration = Duration(milliseconds: 300);

// // Incoming and outgoing AnimatedList items.
// class _ActiveItem implements Comparable<_ActiveItem> {
//   _ActiveItem.incoming(this.controller, this.itemIndex)
//       : removedItemBuilder = null;
//   _ActiveItem.outgoing(
//       this.controller, this.itemIndex, this.removedItemBuilder);
//   _ActiveItem.index(this.itemIndex)
//       : controller = null,
//         removedItemBuilder = null;

//   final AnimationController controller;
//   final AnimatedListRemovedItemBuilder removedItemBuilder;
//   int itemIndex;

//   @override
//   int compareTo(_ActiveItem other) => itemIndex - other.itemIndex;
// }

// /// A scrolling container that animates items when they are inserted or removed.
// ///
// /// This widget's [AnimatedListState] can be used to dynamically insert or
// /// remove items. To refer to the [AnimatedListState] either provide a
// /// [GlobalKey] or use the static [of] method from an item's input callback.
// ///
// /// This widget is similar to one created by [ListView.builder].
// ///
// /// {@youtube 560 315 https://www.youtube.com/watch?v=ZtfItHwFlZ8}
// ///
// /// {@tool dartpad --template=freeform}
// /// This sample application uses an [AnimatedList] to create an effect when
// /// items are removed or added to the list.
// ///
// /// ```dart imports
// /// import 'package:flutter/foundation.dart';
// /// import 'package:flutter/material.dart';
// /// ```
// ///
// /// ```dart
// /// class AnimatedListSample extends StatefulWidget {
// ///   @override
// ///   _AnimatedListSampleState createState() => _AnimatedListSampleState();
// /// }
// ///
// /// class _AnimatedListSampleState extends State<AnimatedListSample> {
// ///   final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
// ///   ListModel<int> _list;
// ///   int _selectedItem;
// ///   int _nextItem; // The next item inserted when the user presses the '+' button.
// ///
// ///   @override
// ///   void initState() {
// ///     super.initState();
// ///     _list = ListModel<int>(
// ///       listKey: _listKey,
// ///       initialItems: <int>[0, 1, 2],
// ///       removedItemBuilder: _buildRemovedItem,
// ///     );
// ///     _nextItem = 3;
// ///   }
// ///
// ///   // Used to build list items that haven't been removed.
// ///   Widget _buildItem(BuildContext context, int index, Animation<double> animation) {
// ///     return CardItem(
// ///       animation: animation,
// ///       item: _list[index],
// ///       selected: _selectedItem == _list[index],
// ///       onTap: () {
// ///         setState(() {
// ///           _selectedItem = _selectedItem == _list[index] ? null : _list[index];
// ///         });
// ///       },
// ///     );
// ///   }
// ///
// ///   // Used to build an item after it has been removed from the list. This
// ///   // method is needed because a removed item remains visible until its
// ///   // animation has completed (even though it's gone as far this ListModel is
// ///   // concerned). The widget will be used by the
// ///   // [AnimatedListState.removeItem] method's
// ///   // [AnimatedListRemovedItemBuilder] parameter.
// ///   Widget _buildRemovedItem(int item, BuildContext context, Animation<double> animation) {
// ///     return CardItem(
// ///       animation: animation,
// ///       item: item,
// ///       selected: false,
// ///       // No gesture detector here: we don't want removed items to be interactive.
// ///     );
// ///   }
// ///
// ///   // Insert the "next item" into the list model.
// ///   void _insert() {
// ///     final int index = _selectedItem == null ? _list.length : _list.indexOf(_selectedItem);
// ///     _list.insert(index, _nextItem++);
// ///   }
// ///
// ///   // Remove the selected item from the list model.
// ///   void _remove() {
// ///     if (_selectedItem != null) {
// ///       _list.removeAt(_list.indexOf(_selectedItem));
// ///       setState(() {
// ///         _selectedItem = null;
// ///       });
// ///     }
// ///   }
// ///
// ///   @override
// ///   Widget build(BuildContext context) {
// ///     return MaterialApp(
// ///       home: Scaffold(
// ///         appBar: AppBar(
// ///           title: const Text('AnimatedList'),
// ///           actions: <Widget>[
// ///             IconButton(
// ///               icon: const Icon(Icons.add_circle),
// ///               onPressed: _insert,
// ///               tooltip: 'insert a new item',
// ///             ),
// ///             IconButton(
// ///               icon: const Icon(Icons.remove_circle),
// ///               onPressed: _remove,
// ///               tooltip: 'remove the selected item',
// ///             ),
// ///           ],
// ///         ),
// ///         body: Padding(
// ///           padding: const EdgeInsets.all(16.0),
// ///           child: AnimatedList(
// ///             key: _listKey,
// ///             initialItemCount: _list.length,
// ///             itemBuilder: _buildItem,
// ///           ),
// ///         ),
// ///       ),
// ///     );
// ///   }
// /// }
// ///
// /// /// Keeps a Dart [List] in sync with an [AnimatedList].
// /// ///
// /// /// The [insert] and [removeAt] methods apply to both the internal list and
// /// /// the animated list that belongs to [listKey].
// /// ///
// /// /// This class only exposes as much of the Dart List API as is needed by the
// /// /// sample app. More list methods are easily added, however methods that
// /// /// mutate the list must make the same changes to the animated list in terms
// /// /// of [AnimatedListState.insertItem] and [AnimatedList.removeItem].
// /// class ListModel<E> {
// ///   ListModel({
// ///     @required this.listKey,
// ///     @required this.removedItemBuilder,
// ///     Iterable<E> initialItems,
// ///   }) : assert(listKey != null),
// ///       assert(removedItemBuilder != null),
// ///       _items = List<E>.from(initialItems ?? <E>[]);
// ///
// ///   final GlobalKey<AnimatedListState> listKey;
// ///   final dynamic removedItemBuilder;
// ///   final List<E> _items;
// ///
// ///   AnimatedListState get _animatedList => listKey.currentState;
// ///
// ///   void insert(int index, E item) {
// ///     _items.insert(index, item);
// ///     _animatedList.insertItem(index);
// ///   }
// ///
// ///   E removeAt(int index) {
// ///     final E removedItem = _items.removeAt(index);
// ///     if (removedItem != null) {
// ///       _animatedList.removeItem(
// ///         index,
// ///           (BuildContext context, Animation<double> animation) => removedItemBuilder(removedItem, context, animation),
// ///       );
// ///     }
// ///     return removedItem;
// ///   }
// ///
// ///   int get length => _items.length;
// ///
// ///   E operator [](int index) => _items[index];
// ///
// ///   int indexOf(E item) => _items.indexOf(item);
// /// }
// ///
// /// /// Displays its integer item as 'item N' on a Card whose color is based on
// /// /// the item's value.
// /// ///
// /// /// The text is displayed in bright green if [selected] is
// /// /// true. This widget's height is based on the [animation] parameter, it
// /// /// varies from 0 to 128 as the animation varies from 0.0 to 1.0.
// /// class CardItem extends StatelessWidget {
// ///   const CardItem({
// ///     Key key,
// ///     @required this.animation,
// ///     this.onTap,
// ///     @required this.item,
// ///     this.selected: false
// ///   }) : assert(animation != null),
// ///        assert(item != null && item >= 0),
// ///        assert(selected != null),
// ///        super(key: key);
// ///
// ///   final Animation<double> animation;
// ///   final VoidCallback onTap;
// ///   final int item;
// ///   final bool selected;
// ///
// ///   @override
// ///   Widget build(BuildContext context) {
// ///     TextStyle textStyle = Theme.of(context).textTheme.headline4;
// ///     if (selected)
// ///       textStyle = textStyle.copyWith(color: Colors.lightGreenAccent[400]);
// ///     return Padding(
// ///       padding: const EdgeInsets.all(2.0),
// ///       child: SizeTransition(
// ///         axis: Axis.vertical,
// ///         sizeFactor: animation,
// ///         child: GestureDetector(
// ///           behavior: HitTestBehavior.opaque,
// ///           onTap: onTap,
// ///           child: SizedBox(
// ///             height: 80.0,
// ///             child: Card(
// ///               color: Colors.primaries[item % Colors.primaries.length],
// ///               child: Center(
// ///                 child: Text('Item $item', style: textStyle),
// ///               ),
// ///             ),
// ///           ),
// ///         ),
// ///       ),
// ///     );
// ///   }
// /// }
// ///
// /// void main() {
// ///   runApp(AnimatedListSample());
// /// }
// /// ```
// /// {@end-tool}
// ///
// /// See also:
// ///
// ///  * [SliverAnimatedList], a sliver that animates items when they are inserted
// ///    or removed from a list.
// class AnimatedList extends StatefulWidget {
//   /// Creates a scrolling container that animates items when they are inserted
//   /// or removed.
//   const AnimatedList({
//     Key? key,
//     required this.itemBuilder,
//     this.initialItemCount = 0,
//     this.scrollDirection = Axis.vertical,
//     this.reverse = false,
//     this.controller,
//     this.primary,
//     this.physics,
//     this.shrinkWrap = false,
//     this.padding,
//   })  : assert(itemBuilder != null),
//         assert(initialItemCount != null && initialItemCount >= 0),
//         super(key: key);

//   /// Called, as needed, to build list item widgets.
//   ///
//   /// List items are only built when they're scrolled into view.
//   ///
//   /// The [AnimatedListItemBuilder] index parameter indicates the item's
//   /// position in the list. The value of the index parameter will be between 0
//   /// and [initialItemCount] plus the total number of items that have been
//   /// inserted with [AnimatedListState.insertItem] and less the total number of
//   /// items that have been removed with [AnimatedListState.removeItem].
//   ///
//   /// Implementations of this callback should assume that
//   /// [AnimatedListState.removeItem] removes an item immediately.
//   final AnimatedListItemBuilder itemBuilder;

//   /// {@template flutter.widgets.animatedList.initialItemCount}
//   /// The number of items the list will start with.
//   ///
//   /// The appearance of the initial items is not animated. They
//   /// are created, as needed, by [itemBuilder] with an animation parameter
//   /// of [kAlwaysCompleteAnimation].
//   /// {@endtemplate}
//   final int initialItemCount;

//   /// The axis along which the scroll view scrolls.
//   ///
//   /// Defaults to [Axis.vertical].
//   final Axis scrollDirection;

//   /// Whether the scroll view scrolls in the reading direction.
//   ///
//   /// For example, if the reading direction is left-to-right and
//   /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from
//   /// left to right when [reverse] is false and from right to left when
//   /// [reverse] is true.
//   ///
//   /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view
//   /// scrolls from top to bottom when [reverse] is false and from bottom to top
//   /// when [reverse] is true.
//   ///
//   /// Defaults to false.
//   final bool reverse;

//   /// An object that can be used to control the position to which this scroll
//   /// view is scrolled.
//   ///
//   /// Must be null if [primary] is true.
//   ///
//   /// A [ScrollController] serves several purposes. It can be used to control
//   /// the initial scroll position (see [ScrollController.initialScrollOffset]).
//   /// It can be used to control whether the scroll view should automatically
//   /// save and restore its scroll position in the [PageStorage] (see
//   /// [ScrollController.keepScrollOffset]). It can be used to read the current
//   /// scroll position (see [ScrollController.offset]), or change it (see
//   /// [ScrollController.animateTo]).
//   final ScrollController controller;

//   /// Whether this is the primary scroll view associated with the parent
//   /// [PrimaryScrollController].
//   ///
//   /// On iOS, this identifies the scroll view that will scroll to top in
//   /// response to a tap in the status bar.
//   ///
//   /// Defaults to true when [scrollDirection] is [Axis.vertical] and
//   /// [controller] is null.
//   final bool primary;

//   /// How the scroll view should respond to user input.
//   ///
//   /// For example, determines how the scroll view continues to animate after the
//   /// user stops dragging the scroll view.
//   ///
//   /// Defaults to matching platform conventions.
//   final ScrollPhysics physics;

//   /// Whether the extent of the scroll view in the [scrollDirection] should be
//   /// determined by the contents being viewed.
//   ///
//   /// If the scroll view does not shrink wrap, then the scroll view will expand
//   /// to the maximum allowed size in the [scrollDirection]. If the scroll view
//   /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must
//   /// be true.
//   ///
//   /// Shrink wrapping the content of the scroll view is significantly more
//   /// expensive than expanding to the maximum allowed size because the content
//   /// can expand and contract during scrolling, which means the size of the
//   /// scroll view needs to be recomputed whenever the scroll position changes.
//   ///
//   /// Defaults to false.
//   final bool shrinkWrap;

//   /// The amount of space by which to inset the children.
//   final EdgeInsetsGeometry padding;

//   /// The state from the closest instance of this class that encloses the given
//   /// context.
//   ///
//   /// This method is typically used by [AnimatedList] item widgets that insert
//   /// or remove items in response to user input.
//   ///
//   /// ```dart
//   /// AnimatedListState animatedList = AnimatedList.of(context);
//   /// ```
//   static AnimatedListState of(BuildContext context, {bool nullOk = false}) {
//     assert(context != null);
//     assert(nullOk != null);
//     final AnimatedListState result =
//         context.findAncestorStateOfType<AnimatedListState>();
//     if (nullOk || result != null) return result;
//     throw FlutterError.fromParts(<DiagnosticsNode>[
//       ErrorSummary(
//           'AnimatedList.of() called with a context that does not contain an AnimatedList.'),
//       ErrorDescription(
//           'No AnimatedList ancestor could be found starting from the context that was passed to AnimatedList.of().'),
//       ErrorHint(
//           'This can happen when the context provided is from the same StatefulWidget that '
//           'built the AnimatedList. Please see the AnimatedList documentation for examples '
//           'of how to refer to an AnimatedListState object:'
//           '  https://api.flutter.dev/flutter/widgets/AnimatedListState-class.html'),
//       context.describeElement('The context used was')
//     ]);
//   }

//   @override
//   AnimatedListState createState() => AnimatedListState();
// }

// /// The state for a scrolling container that animates items when they are
// /// inserted or removed.
// ///
// /// When an item is inserted with [insertItem] an animation begins running. The
// /// animation is passed to [AnimatedList.itemBuilder] whenever the item's widget
// /// is needed.
// ///
// /// When an item is removed with [removeItem] its animation is reversed.
// /// The removed item's animation is passed to the [removeItem] builder
// /// parameter.
// ///
// /// An app that needs to insert or remove items in response to an event
// /// can refer to the [AnimatedList]'s state with a global key:
// ///
// /// ```dart
// /// GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
// /// ...
// /// AnimatedList(key: listKey, ...);
// /// ...
// /// listKey.currentState.insert(123);
// /// ```
// ///
// /// [AnimatedList] item input handlers can also refer to their [AnimatedListState]
// /// with the static [AnimatedList.of] method.
// class AnimatedListState extends State<AnimatedList>
//     with TickerProviderStateMixin<AnimatedList> {
//   final GlobalKey<SliverAnimatedListState> _sliverAnimatedListKey = GlobalKey();

//   /// Insert an item at [index] and start an animation that will be passed
//   /// to [AnimatedList.itemBuilder] when the item is visible.
//   ///
//   /// This method's semantics are the same as Dart's [List.insert] method:
//   /// it increases the length of the list by one and shifts all items at or
//   /// after [index] towards the end of the list.
//   void insertItem(int index, {Duration duration = _kDuration}) {
//     _sliverAnimatedListKey.currentState.insertItem(index, duration: duration);
//   }

//   /// Remove the item at [index] and start an animation that will be passed
//   /// to [builder] when the item is visible.
//   ///
//   /// Items are removed immediately. After an item has been removed, its index
//   /// will no longer be passed to the [AnimatedList.itemBuilder]. However the
//   /// item will still appear in the list for [duration] and during that time
//   /// [builder] must construct its widget as needed.
//   ///
//   /// This method's semantics are the same as Dart's [List.remove] method:
//   /// it decreases the length of the list by one and shifts all items at or
//   /// before [index] towards the beginning of the list.
//   void removeItem(int index, AnimatedListRemovedItemBuilder builder,
//       {Duration duration = _kDuration}) {
//     _sliverAnimatedListKey.currentState
//         .removeItem(index, builder, duration: duration);
//   }

//   @override
//   Widget build(BuildContext context) {
//     return CustomScrollView(
//       scrollDirection: widget.scrollDirection,
//       reverse: widget.reverse,
//       controller: widget.controller,
//       primary: widget.primary,
//       physics: widget.physics,
//       shrinkWrap: widget.shrinkWrap,
//       slivers: <Widget>[
//         SliverPadding(
//           padding: widget.padding ?? const EdgeInsets.all(0),
//           sliver: SliverAnimatedList(
//             key: _sliverAnimatedListKey,
//             itemBuilder: widget.itemBuilder,
//             initialItemCount: widget.initialItemCount,
//           ),
//         ),
//       ],
//     );
//   }
// }

// /// A sliver that animates items when they are inserted or removed.
// ///
// /// This widget's [SliverAnimatedListState] can be used to dynamically insert or
// /// remove items. To refer to the [SliverAnimatedListState] either provide a
// /// [GlobalKey] or use the static [SliverAnimatedList.of] method from an item's
// /// input callback.
// ///
// /// {@tool dartpad --template=freeform}
// /// This sample application uses a [SliverAnimatedList] to create an animated
// /// effect when items are removed or added to the list.
// ///
// /// ```dart imports
// /// import 'package:flutter/foundation.dart';
// /// import 'package:flutter/material.dart';
// /// ```
// ///
// /// ```dart
// /// void main() => runApp(SliverAnimatedListSample());
// ///
// /// class SliverAnimatedListSample extends StatefulWidget {
// ///   @override
// ///   _SliverAnimatedListSampleState createState() => _SliverAnimatedListSampleState();
// /// }
// ///
// /// class _SliverAnimatedListSampleState extends State<SliverAnimatedListSample> {
// ///   final GlobalKey<SliverAnimatedListState> _listKey = GlobalKey<SliverAnimatedListState>();
// ///   final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
// ///   ListModel<int> _list;
// ///   int _selectedItem;
// ///   int _nextItem; // The next item inserted when the user presses the '+' button.
// ///
// ///   @override
// ///   void initState() {
// ///     super.initState();
// ///     _list = ListModel<int>(
// ///       listKey: _listKey,
// ///       initialItems: <int>[0, 1, 2],
// ///       removedItemBuilder: _buildRemovedItem,
// ///     );
// ///     _nextItem = 3;
// ///   }
// ///
// ///   // Used to build list items that haven't been removed.
// ///   Widget _buildItem(BuildContext context, int index, Animation<double> animation) {
// ///     return CardItem(
// ///       animation: animation,
// ///       item: _list[index],
// ///       selected: _selectedItem == _list[index],
// ///       onTap: () {
// ///         setState(() {
// ///           _selectedItem = _selectedItem == _list[index] ? null : _list[index];
// ///         });
// ///       },
// ///     );
// ///   }
// ///
// ///   // Used to build an item after it has been removed from the list. This
// ///   // method is needed because a removed item remains visible until its
// ///   // animation has completed (even though it's gone as far this ListModel is
// ///   // concerned). The widget will be used by the
// ///   // [AnimatedListState.removeItem] method's
// ///   // [AnimatedListRemovedItemBuilder] parameter.
// ///   Widget _buildRemovedItem(int item, BuildContext context, Animation<double> animation) {
// ///     return CardItem(
// ///       animation: animation,
// ///       item: item,
// ///       selected: false,
// ///     );
// ///   }
// ///
// ///   // Insert the "next item" into the list model.
// ///   void _insert() {
// ///     final int index = _selectedItem == null ? _list.length : _list.indexOf(_selectedItem);
// ///     _list.insert(index, _nextItem++);
// ///   }
// ///
// ///   // Remove the selected item from the list model.
// ///   void _remove() {
// ///     if (_selectedItem != null) {
// ///       _list.removeAt(_list.indexOf(_selectedItem));
// ///       setState(() {
// ///         _selectedItem = null;
// ///       });
// ///     } else {
// ///       _scaffoldKey.currentState.showSnackBar(SnackBar(
// ///         content: Text(
// ///           'Select an item to remove from the list.',
// ///           style: TextStyle(fontSize: 20),
// ///         ),
// ///       ));
// ///     }
// ///   }
// ///
// ///   @override
// ///   Widget build(BuildContext context) {
// ///     return MaterialApp(
// ///       home: Scaffold(
// ///         key: _scaffoldKey,
// ///         body: CustomScrollView(
// ///           slivers: <Widget>[
// ///             SliverAppBar(
// ///               title: Text(
// ///                 'SliverAnimatedList',
// ///                 style: TextStyle(fontSize: 30),
// ///               ),
// ///               expandedHeight: 60,
// ///               centerTitle: true,
// ///               backgroundColor: Colors.amber[900],
// ///               leading: IconButton(
// ///                 icon: const Icon(Icons.add_circle),
// ///                 onPressed: _insert,
// ///                 tooltip: 'Insert a new item.',
// ///                 iconSize: 32,
// ///               ),
// ///               actions: [
// ///                 IconButton(
// ///                   icon: const Icon(Icons.remove_circle),
// ///                   onPressed: _remove,
// ///                   tooltip: 'Remove the selected item.',
// ///                   iconSize: 32,
// ///                 ),
// ///               ],
// ///             ),
// ///             SliverAnimatedList(
// ///               key: _listKey,
// ///               initialItemCount: _list.length,
// ///               itemBuilder: _buildItem,
// ///             ),
// ///           ],
// ///         ),
// ///       ),
// ///     );
// ///   }
// /// }
// ///
// /// // Keeps a Dart [List] in sync with an [AnimatedList].
// /// //
// /// // The [insert] and [removeAt] methods apply to both the internal list and
// /// // the animated list that belongs to [listKey].
// /// //
// /// // This class only exposes as much of the Dart List API as is needed by the
// /// // sample app. More list methods are easily added, however methods that
// /// // mutate the list must make the same changes to the animated list in terms
// /// // of [AnimatedListState.insertItem] and [AnimatedList.removeItem].
// /// class ListModel<E> {
// ///   ListModel({
// ///     @required this.listKey,
// ///     @required this.removedItemBuilder,
// ///     Iterable<E> initialItems,
// ///   }) : assert(listKey != null),
// ///        assert(removedItemBuilder != null),
// ///        _items = List<E>.from(initialItems ?? <E>[]);
// ///
// ///   final GlobalKey<SliverAnimatedListState> listKey;
// ///   final dynamic removedItemBuilder;
// ///   final List<E> _items;
// ///
// ///   SliverAnimatedListState get _animatedList => listKey.currentState;
// ///
// ///   void insert(int index, E item) {
// ///     _items.insert(index, item);
// ///     _animatedList.insertItem(index);
// ///   }
// ///
// ///   E removeAt(int index) {
// ///     final E removedItem = _items.removeAt(index);
// ///     if (removedItem != null) {
// ///       _animatedList.removeItem(
// ///         index,
// ///         (BuildContext context, Animation<double> animation) => removedItemBuilder(removedItem, context, animation),
// ///       );
// ///     }
// ///     return removedItem;
// ///   }
// ///
// ///   int get length => _items.length;
// ///
// ///   E operator [](int index) => _items[index];
// ///
// ///   int indexOf(E item) => _items.indexOf(item);
// /// }
// ///
// /// // Displays its integer item as 'Item N' on a Card whose color is based on
// /// // the item's value.
// /// //
// /// // The card turns gray when [selected] is true. This widget's height
// /// // is based on the [animation] parameter. It varies as the animation value
// /// // transitions from 0.0 to 1.0.
// /// class CardItem extends StatelessWidget {
// ///   const CardItem({
// ///     Key key,
// ///     @required this.animation,
// ///     @required this.item,
// ///     this.onTap,
// ///     this.selected = false,
// ///   }) : assert(animation != null),
// ///        assert(item != null && item >= 0),
// ///        assert(selected != null),
// ///        super(key: key);
// ///
// ///   final Animation<double> animation;
// ///   final VoidCallback onTap;
// ///   final int item;
// ///   final bool selected;
// ///
// ///   @override
// ///   Widget build(BuildContext context) {
// ///     return Padding(
// ///       padding:
// ///       const EdgeInsets.only(
// ///         left: 2.0,
// ///         right: 2.0,
// ///         top: 2.0,
// ///         bottom: 0.0,
// ///       ),
// ///       child: SizeTransition(
// ///         axis: Axis.vertical,
// ///         sizeFactor: animation,
// ///         child: GestureDetector(
// ///           onTap: onTap,
// ///           child: SizedBox(
// ///             height: 80.0,
// ///             child: Card(
// ///               color: selected
// ///                 ? Colors.black12
// ///                 : Colors.primaries[item % Colors.primaries.length],
// ///               child: Center(
// ///                 child: Text(
// ///                   'Item $item',
// ///                   style: Theme.of(context).textTheme.headline4,
// ///                 ),
// ///               ),
// ///             ),
// ///           ),
// ///         ),
// ///       ),
// ///     );
// ///   }
// /// }
// /// ```
// /// {@end-tool}
// ///
// /// See also:
// ///
// ///  * [SliverList], which does not animate items when they are inserted or
// ///    removed.
// ///  * [AnimatedList], a non-sliver scrolling container that animates items when
// ///    they are inserted or removed.
// class SliverAnimatedList extends StatefulWidget {
//   /// Creates a sliver that animates items when they are inserted or removed.
//   const SliverAnimatedList({
//     Key key,
//     @required this.itemBuilder,
//     this.initialItemCount = 0,
//   })  : assert(itemBuilder != null),
//         assert(initialItemCount != null && initialItemCount >= 0),
//         super(key: key);

//   /// Called, as needed, to build list item widgets.
//   ///
//   /// List items are only built when they're scrolled into view.
//   ///
//   /// The [AnimatedListItemBuilder] index parameter indicates the item's
//   /// position in the list. The value of the index parameter will be between 0
//   /// and [initialItemCount] plus the total number of items that have been
//   /// inserted with [SliverAnimatedListState.insertItem] and less the total
//   /// number of items that have been removed with
//   /// [SliverAnimatedListState.removeItem].
//   ///
//   /// Implementations of this callback should assume that
//   /// [SliverAnimatedListState.removeItem] removes an item immediately.
//   final AnimatedListItemBuilder itemBuilder;

//   /// {@macro flutter.widgets.animatedList.initialItemCount}
//   final int initialItemCount;

//   @override
//   SliverAnimatedListState createState() => SliverAnimatedListState();

//   /// The state from the closest instance of this class that encloses the given
//   /// context.
//   ///
//   /// This method is typically used by [SliverAnimatedList] item widgets that
//   /// insert or remove items in response to user input.
//   ///
//   /// ```dart
//   /// SliverAnimatedListState animatedList = SliverAnimatedList.of(context);
//   /// ```
//   static SliverAnimatedListState of(BuildContext context,
//       {bool nullOk = false}) {
//     assert(context != null);
//     assert(nullOk != null);
//     final SliverAnimatedListState result =
//         context.findAncestorStateOfType<SliverAnimatedListState>();
//     if (nullOk || result != null) return result;
//     throw FlutterError(
//         'SliverAnimatedList.of() called with a context that does not contain a SliverAnimatedList.\n'
//         'No SliverAnimatedListState ancestor could be found starting from the '
//         'context that was passed to SliverAnimatedListState.of(). This can '
//         'happen when the context provided is from the same StatefulWidget that '
//         'built the AnimatedList. Please see the SliverAnimatedList documentation '
//         'for examples of how to refer to an AnimatedListState object: '
//         'https://docs.flutter.io/flutter/widgets/SliverAnimatedListState-class.html \n'
//         'The context used was:\n'
//         '  $context');
//   }
// }

// /// The state for a sliver that animates items when they are
// /// inserted or removed.
// ///
// /// When an item is inserted with [insertItem] an animation begins running. The
// /// animation is passed to [SliverAnimatedList.itemBuilder] whenever the item's
// /// widget is needed.
// ///
// /// When an item is removed with [removeItem] its animation is reversed.
// /// The removed item's animation is passed to the [removeItem] builder
// /// parameter.
// ///
// /// An app that needs to insert or remove items in response to an event
// /// can refer to the [SliverAnimatedList]'s state with a global key:
// ///
// /// ```dart
// /// GlobalKey<SliverAnimatedListState> listKey = GlobalKey<SliverAnimatedListState>();
// /// ...
// /// SliverAnimatedList(key: listKey, ...);
// /// ...
// /// listKey.currentState.insert(123);
// /// ```
// ///
// /// [SliverAnimatedList] item input handlers can also refer to their
// /// [SliverAnimatedListState] with the static [SliverAnimatedList.of] method.
// class SliverAnimatedListState extends State<SliverAnimatedList>
//     with TickerProviderStateMixin {
//   final List<_ActiveItem> _incomingItems = <_ActiveItem>[];
//   final List<_ActiveItem> _outgoingItems = <_ActiveItem>[];
//   int _itemsCount = 0;

//   @override
//   void initState() {
//     super.initState();
//     _itemsCount = widget.initialItemCount;
//   }

//   @override
//   void dispose() {
//     for (final _ActiveItem item in _incomingItems.followedBy(_outgoingItems)) {
//       item.controller.dispose();
//     }
//     super.dispose();
//   }

//   _ActiveItem _removeActiveItemAt(List<_ActiveItem> items, int itemIndex) {
//     final int i = binarySearch(items, _ActiveItem.index(itemIndex));
//     return i == -1 ? null : items.removeAt(i);
//   }

//   _ActiveItem _activeItemAt(List<_ActiveItem> items, int itemIndex) {
//     final int i = binarySearch(items, _ActiveItem.index(itemIndex));
//     return i == -1 ? null : items[i];
//   }

//   // The insertItem() and removeItem() index parameters are defined as if the
//   // removeItem() operation removed the corresponding list entry immediately.
//   // The entry is only actually removed from the ListView when the remove animation
//   // finishes. The entry is added to _outgoingItems when removeItem is called
//   // and removed from _outgoingItems when the remove animation finishes.

//   int _indexToItemIndex(int index) {
//     int itemIndex = index;
//     for (final _ActiveItem item in _outgoingItems) {
//       if (item.itemIndex <= itemIndex)
//         itemIndex += 1;
//       else
//         break;
//     }
//     return itemIndex;
//   }

//   int _itemIndexToIndex(int itemIndex) {
//     int index = itemIndex;
//     for (final _ActiveItem item in _outgoingItems) {
//       assert(item.itemIndex != itemIndex);
//       if (item.itemIndex < itemIndex)
//         index -= 1;
//       else
//         break;
//     }
//     return index;
//   }

//   SliverChildDelegate _createDelegate() {
//     return SliverChildBuilderDelegate(_itemBuilder, childCount: _itemsCount);
//   }

//   /// Insert an item at [index] and start an animation that will be passed to
//   /// [SliverAnimatedList.itemBuilder] when the item is visible.
//   ///
//   /// This method's semantics are the same as Dart's [List.insert] method:
//   /// it increases the length of the list by one and shifts all items at or
//   /// after [index] towards the end of the list.
//   void insertItem(int index, {Duration duration = _kDuration}) {
//     assert(index != null && index >= 0);
//     assert(duration != null);

//     final int itemIndex = _indexToItemIndex(index);
//     assert(itemIndex >= 0 && itemIndex <= _itemsCount);

//     // Increment the incoming and outgoing item indices to account
//     // for the insertion.
//     for (final _ActiveItem item in _incomingItems) {
//       if (item.itemIndex >= itemIndex) item.itemIndex += 1;
//     }
//     for (final _ActiveItem item in _outgoingItems) {
//       if (item.itemIndex >= itemIndex) item.itemIndex += 1;
//     }

//     final AnimationController controller = AnimationController(
//       duration: duration,
//       vsync: this,
//     );
//     final _ActiveItem incomingItem = _ActiveItem.incoming(
//       controller,
//       itemIndex,
//     );
//     setState(() {
//       _incomingItems
//         ..add(incomingItem)
//         ..sort();
//       _itemsCount += 1;
//     });

//     controller.forward().then<void>((_) {
//       _removeActiveItemAt(_incomingItems, incomingItem.itemIndex)
//           .controller
//           .dispose();
//     });
//   }

//   /// Remove the item at [index] and start an animation that will be passed
//   /// to [builder] when the item is visible.
//   ///
//   /// Items are removed immediately. After an item has been removed, its index
//   /// will no longer be passed to the [SliverAnimatedList.itemBuilder]. However
//   /// the item will still appear in the list for [duration] and during that time
//   /// [builder] must construct its widget as needed.
//   ///
//   /// This method's semantics are the same as Dart's [List.remove] method:
//   /// it decreases the length of the list by one and shifts all items at or
//   /// before [index] towards the beginning of the list.
//   void removeItem(int index, AnimatedListRemovedItemBuilder builder,
//       {Duration duration = _kDuration}) {
//     assert(index != null && index >= 0);
//     assert(builder != null);
//     assert(duration != null);

//     final int itemIndex = _indexToItemIndex(index);
//     assert(itemIndex >= 0 && itemIndex < _itemsCount);
//     assert(_activeItemAt(_outgoingItems, itemIndex) == null);

//     final _ActiveItem incomingItem =
//         _removeActiveItemAt(_incomingItems, itemIndex);
//     final AnimationController controller = incomingItem?.controller ??
//         AnimationController(duration: duration, value: 1.0, vsync: this);
//     final _ActiveItem outgoingItem =
//         _ActiveItem.outgoing(controller, itemIndex, builder);
//     setState(() {
//       _outgoingItems
//         ..add(outgoingItem)
//         ..sort();
//     });

//     controller.reverse().then<void>((void value) {
//       _removeActiveItemAt(_outgoingItems, outgoingItem.itemIndex)
//           .controller
//           .dispose();

//       // Decrement the incoming and outgoing item indices to account
//       // for the removal.
//       for (final _ActiveItem item in _incomingItems) {
//         if (item.itemIndex > outgoingItem.itemIndex) item.itemIndex -= 1;
//       }
//       for (final _ActiveItem item in _outgoingItems) {
//         if (item.itemIndex > outgoingItem.itemIndex) item.itemIndex -= 1;
//       }

//       setState(() => _itemsCount -= 1);
//     });
//   }

//   Widget _itemBuilder(BuildContext context, int itemIndex) {
//     final _ActiveItem outgoingItem = _activeItemAt(_outgoingItems, itemIndex);
//     if (outgoingItem != null) {
//       return outgoingItem.removedItemBuilder(
//         context,
//         outgoingItem.controller.view,
//       );
//     }

//     final _ActiveItem incomingItem = _activeItemAt(_incomingItems, itemIndex);
//     final Animation<double> animation =
//         incomingItem?.controller?.view ?? kAlwaysCompleteAnimation;
//     return widget.itemBuilder(
//       context,
//       _itemIndexToIndex(itemIndex),
//       animation,
//     );
//   }

//   @override
//   Widget build(BuildContext context) {
//     return SliverList(
//       delegate: _createDelegate(),
//     );
//   }

//   /// 全部清空无动画效果,用于下拉刷新
//   ///
//   refresh(int itemCount) {
//     _itemsCount = itemCount;
//     setState(() {});
//   }
// }
